package org.example;

import org.example.model.Author;
import org.example.model.Book;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * testStreamMidOf: 测试流中间操作
 * testStreamEndOf: 测试六的终极操作
 * @author shenguangyang
 */
public class StreamTest {

    /**
     * 答应所有年龄小于19的作家名称, 并且注意要去重
     */
    @Test
    public void test01() {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .filter(author -> author.getAge() < 18)
                .forEach(author -> System.out.println(author.getName()));
    }

    @Test
    public void testMapCollectToStream() {
        Map<String, Integer> map = new HashMap<>();
        map.put("蜡笔小新", 19);
        map.put("黑子", 17);
        map.put("日向", 16);
        map.entrySet().stream()
                .filter(entry -> entry.getValue() > 16)
                .forEach(entry -> System.out.println(entry.getKey()));
    }

    @Test
    public void testStreamMidOfMap() {
        List<Author> authors = getAuthors();
        authors.stream()
                .map(Author::getName)
                .map(name -> "[ " + name + " ]")
                .forEach(System.out::println);
    }

    /**
     * distinct 判断依据是 equals方法
     */
    @Test
    public void testStreamMidOfDistinct() {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getName()));
    }

    @Test
    public void testStreamMidOfSort() {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
//                .sorted()
                .sorted((o1, o2) -> o2.getAge().compareTo(o1.getAge()))
                .forEach(author -> System.out.println(author.getAge()));
    }

    @Test
    public void testStreamMidOfLimit() {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted()
                .limit(2)
                .forEach(author -> System.out.println(author.getAge()));
    }

    @Test
    public void testStreamMidOfSkip() {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted()
                // 跳过前2个元素
                .skip(2)
                .forEach(author -> System.out.println(author.getAge()));
    }

    /**
     * 将一个对象转换成多个对象
     */
    @Test
    public void testStreamMidOfFlatMap() {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .forEach(book -> System.out.println(book.getCategory()));
        System.out.println("------------------------");
        authors.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                .forEach(System.out::println);
    }

    @Test
    public void testStreamEndOfForEach() {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .map(Author::getName)
                .forEach(System.out::println);
    }

    @Test
    public void testStreamEndOfCount() {
        List<Author> authors = getAuthors();
        long count = authors.stream()
                .distinct()
                .map(Author::getName)
                .count();
        System.out.println(count);
    }

    @Test
    public void testStreamEndOfMinAndMax() {
        List<Author> authors = getAuthors();
        Optional<Double> max = authors.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .map(Book::getScore)
                .max(Double::compareTo);
        System.out.println("max: " + max.orElse(0.0));

        Optional<Double> min = authors.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .map(Book::getScore)
                .min(Double::compareTo);
        System.out.println("min: " + min.orElse(0.0));
    }

    @Test
    public void testStreamEndOfCollect() {
        List<Author> authors = getAuthors();
        // 获取一个存放所有作者名字的Lit集合
        List<String> nameList = authors.stream()
                .distinct()
                .map(Author::getName)
                .collect(Collectors.toList());
        System.out.println("authorNameList: " + nameList);

        // 获取一个存放所有书名的Set集合
        Set<String> nameSet = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(Book::getName)
                .collect(Collectors.toSet());
        System.out.println("bookNameSet: " + nameSet);

        // 获取一个Map集合, key: 作者名称, value: List<Book>
        Map<String, List<Book>> map = authors.stream()
                .distinct()
                .collect(Collectors.toMap(Author::getName, Author::getBooks, (key1, key2) -> key2));
        System.out.println("bookNameSet: " + nameSet);

    }

    // ========================================== 终结操作之查找与匹配
    @Test
    public void testStreamEndOfAnyMatch() {
        // 是否有任意年龄在18岁之上的作者
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .anyMatch(author -> author.getAge() > 18);
        System.out.println(flag);
    }

    @Test
    public void testStreamEndOfAllMatch() {
        // 是否有所有年龄在18岁之上的作者
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .allMatch(author -> author.getAge() > 18);
        System.out.println(flag);
    }

    @Test
    public void testStreamEndOfNoneMatch() {
        // 判断所有作者都没有操作29岁
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .noneMatch(author -> author.getAge() > 29);
        System.out.println(flag);
    }

    @Test
    public void testStreamEndOfFindFirst() {
        // 判断所有作者都没有操作29岁
        List<Author> authors = getAuthors();
        Author author = authors.stream()
                .sorted((o1, o2) -> o2.getAge().compareTo(o1.getAge()))
                .findFirst().orElse(null);
        System.out.println(author);
    }

    @Test
    public void testStreamEndOfFindAny() {
        List<Author> authors = getAuthors();
        Author result = authors.stream()
                .filter(author -> author.getAge() > 18)
                .findAny().orElse(null);
        System.out.println(result);
    }

    // ==================================== 归并
    /**
     * 对六中的数据按照你制定的计算方式计算出一个结果
     */
    @Test
    public void testStreamEndOfReduce() {
        List<Author> authors = getAuthors();
        // 求所有作者年龄的和
        Integer ageSumReduce = authors.stream()
                .distinct()
                .map(Author::getAge)
                // 第一个参数: 初始化值, 第二个参数: 不断对初始化值操作
                .reduce(0, (result, element) -> result + element);
        System.out.println("ageSum: " + ageSumReduce);

        // 求所有作者年龄中的最大值
        Integer ageMaxReduce = authors.stream()
                .distinct()
                .map(Author::getAge)
                .reduce(Integer.MIN_VALUE, (result, element) -> result > element ? result : element);
        System.out.println("ageMax: " + ageMaxReduce);

        // 求所有作者年龄中的最小值
        Integer ageMinReduce = authors.stream()
                .distinct()
                .map(Author::getAge)
                .reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
        System.out.println("ageMin: " + ageMinReduce);
    }

    /**
     * 对流中类型转换的优化
     */
    @Test
    public void testStreamOptimizationOfTypeConvert() {
        List<Author> authors = getAuthors();
        // 过多类型自动拆箱转换, 对性能有一点损耗
        authors.stream()
                .map(Author::getAge)
                .map(age -> age + 10)
                .filter(age -> age > 10)
                .map(age -> age + 2)
                .forEach(System.out::println);

        System.out.println("----------------------- 优化");
        authors.stream()
                .mapToInt(Author::getAge)
                // 之后传入的是int类型, 不需要自动拆箱转换
                .map(age -> age + 10)
                .filter(age -> age > 10)
                .map(age -> age + 2)
                .forEach(System.out::println);
    }

    /**
     * 测试并行流
     * 当流中有大量元素时，我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完全。如果我们自己去用代码
     * 实现的话其实会非常的复杂，并且要求你对并发编程有足够的理解和认识。而如果我们使用Stream的话，我们只需要修改一个方法的调
     * 用就可以使用并行流来帮我们实现，从而提高效率。
     */
    @Test
    public void testStreamOfParallel() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 9, 10);
        Integer sum = stream
                // 把串行流转成并行流
                .parallel()
                .peek(num -> System.out.println(Thread.currentThread().getName() + " -- " + num))
                .filter(num -> num > 5)
                .reduce(Integer::sum)
                .orElse(0);
        System.out.println(sum);
    }

    /**
     * 测试函数接口
     */
    @Test
    public void testFunction() {
        List<Author> authors = getAuthors();
        authors.stream()
                .filter(
                        ((Predicate<Author>) author -> author.getAge() > 17)
                        .and(author -> author.getName().length() > 2)
                ).forEach(author -> System.out.println(author.getAge() + "-" + author.getName()));
    }

    // 初始化一些数据
    private static List<Author> getAuthors() {
        Author author1 = new Author(1L, "杨杰炜", "my introduction 1", 18, null);
        Author author2 = new Author(2L, "yjw", "my introduction 2", 19, null);
        Author author3 = new Author(2L, "yjw", "my introduction 2", 19, null);
        Author author4 = new Author(4L, "wdt", "my introduction 4", 29, null);
        Author author5 = new Author(5L, "wtf", "my introduction 5", 12, null);

        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        // 上面是作者和书
        books1.add(new Book(1L, "类别,分类啊", "书名1", 45D, "这是简介哦"));
        books1.add(new Book(2L, "高效", "书名2", 84D, "这是简介哦"));
        books1.add(new Book(3L, "喜剧", "书名3", 83D, "这是简介哦"));

        books2.add(new Book(5L, "天啊", "书名4", 65D, "这是简介哦"));
        books2.add(new Book(6L, "高效", "书名5", 89D, "这是简介哦"));

        books3.add(new Book(7L, "久啊", "书名6", 45D, "这是简介哦"));
        books3.add(new Book(8L, "高效", "书名7", 44D, "这是简介哦"));
        books3.add(new Book(9L, "喜剧", "书名8", 81D, "这是简介哦"));

        author1.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books2);
        author4.setBooks(books3);
        author5.setBooks(books2);

        return new ArrayList<>(Arrays.asList(author1, author2, author3, author4, author5));
    }
}
